/*
 * Copyright (C) 2000-2008 Jens Nixdorf and various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies of the Software, its documentation and marketing & publicity
 * materials, and acknowledgment shall be given in the documentation, materials
 * and software packages that this Software was used.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package org.mopore.jat.util;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ColorConvertOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.awt.image.RescaleOp;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mopore.jat.core.messaging.MessageType;
import org.mopore.jat.gui.Theme;

public class ImageService {
	
	private static final int TWELVE = 12;
	private static final Font TEXT_FONT = new Font( null, Font.PLAIN, TWELVE);
	private static final Color TEXT_COLOR = new Color( 210, 210, 210 );
	private static final Color voidColor = new Color(0, 0, 0, 0);
	private static final int MAX_TEXT_WIDTH = 14;
	
	private static final Map<String, Font> perfectFonts = 
		new HashMap<String, Font>();
	
	
	public static BufferedImage createTrayIconImage( String text, Theme theme  ) {
		
		int width = 16;
		int height = 16;

		// create image an Graphics2D
		BufferedImage image = new BufferedImage( width, height, Transparency.BITMASK);
		Graphics2D g = image.createGraphics();
		g.setRenderingHint(RenderingHints.KEY_RENDERING,
				RenderingHints.VALUE_RENDER_QUALITY);
		
		// create transparent background
		g.setComposite( AlphaComposite.Src );
		g.setColor( voidColor );
		g.fillRect(0, 0, width, height);
		
		
		// draw frame
		g.setPaint( 
				new GradientPaint( 
						0, 0, theme.getColorBottom(),
						0, 15, theme.getColorTop()
				)
		);		
		g.drawLine( 1, 0, 14, 0 );
		g.drawLine( 0, 1, 0, 14 );
		g.drawLine( 15, 1, 15, 14 );
		g.drawLine( 1, 15, 14, 15 );
		
		// draw inner content
        g.setPaint( 
                new GradientPaint( 
                    0, 0, theme.getColorTop(),
                    0, 15, theme.getColorBottom()
                )
            );
		g.fillRect( 1, 1, 14, 14 );
		
		// render text
		g.setColor( TEXT_COLOR );
		
		Font font = assertMaxTextWidth( text, g );
		FontRenderContext frc = g.getFontRenderContext();
		Rectangle2D rectangle = font.getStringBounds( text, frc );
		int textWidth = (int) rectangle.getWidth() - 1;
		int textHeight = (int) rectangle.getHeight() - 1;
		
		
		g.setFont( font );
		
		int x = 7 - ( textWidth / 2 );
		int y = 7 + ( textHeight / 2 );
		if ( text.length() == 2 ) {
			y--;
		}
		g.drawString( text, x, y );

		g.dispose();
		
		return image;
	}
	
	/**
	 * @param text
	 * @param g 
	 * @return
	 */
	private static Font assertMaxTextWidth(String text, Graphics2D g) {
		
		Font font = perfectFonts.get( text );
		if ( font == null ) {
		
			font = TEXT_FONT;
			FontRenderContext frc = g.getFontRenderContext();
			Rectangle2D rectangle = font.getStringBounds( text, frc );
			float fontSize = font.getSize();
			
			double textWidth = rectangle.getWidth();
			while ( textWidth > MAX_TEXT_WIDTH ) {
				fontSize -= 0.5;
				font = font.deriveFont( fontSize );
				rectangle = font.getStringBounds( text, frc );
				textWidth = rectangle.getWidth();
			}
			
			perfectFonts.put( text, font );
		}
		
		return font;
	}

	public static BufferedImage load( String path ) {
		URL url = ImageService.class.getResource( path );
		
		BufferedImage image;
		try {
			InputStream inputStream = url.openStream();
			image = ImageIO.read(inputStream );
		} catch (Exception e) {
			Log log = LogFactory.getLog( ImageService.class.getName() );
			log.error( "can not load image: " + path, e );
			throw new RuntimeException( e );
		}
		
		return image;
	}
	
	
	public static BufferedImage blur( BufferedImage source, float blurFactor ){
		float factor = 1.0f / blurFactor;
		float[] blurKernel = {
		     factor, factor, factor,
		     factor, factor, factor,
		     factor, factor, factor
		};
		BufferedImageOp blur = new ConvolveOp(new Kernel(3, 3, blurKernel));
		source = blur.filter( source, null);
		return source;
	}
	
	
	
	public static BufferedImage blackWhite( BufferedImage source ){
		
		ColorSpace colorSpace = ColorSpace.getInstance( ColorSpace.CS_GRAY );
		ColorConvertOp convertOp = new ColorConvertOp( colorSpace, null );
		source = convertOp.filter( source, null);
		return source;
	}
	
	
	public static BufferedImage moreYellowAndDarker( BufferedImage source ){
		
		float[] factors = new float[] {
			    0.8f, 0.8f, 0.8f
			};
			float[] offsets = new float[] {
			    60.0f, 60.0f, 0.0f
			};
		RescaleOp op = new RescaleOp(factors, offsets, null);
		source = op.filter(source, null);
		return source;
	}


	//returns target
	public static BufferedImage copy(BufferedImage source, BufferedImage target) {
		Graphics2D g2 = target.createGraphics();
		g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
		g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		double scalex = (double) target.getWidth()/ source.getWidth();
		double scaley = (double) target.getHeight()/ source.getHeight();
		AffineTransform xform = AffineTransform.getScaleInstance(scalex, scaley);
		g2.drawRenderedImage(source, xform);
		g2.dispose();
		return target;
	}

	public static BufferedImage scaleWithContraints( BufferedImage image, int maxWidth, int maxHeight ) {
		
		int newWidth;
		int newHeight;
		
		Graphics2D graphics = image.createGraphics();
		GraphicsConfiguration gc = graphics.getDeviceConfiguration();
		graphics.dispose();
		int transparency = image.getColorModel().getTransparency();
		
		double currentWidth = image.getWidth();
		double currentHeight = image.getHeight();
		double ratio = currentWidth / currentHeight;
		
		// horzontal trial
		double hWidth = maxWidth;
		double hHeight = maxWidth / ratio; 
		
		boolean isOkayWithMaxHeight = maxHeight >= hHeight;
		if ( isOkayWithMaxHeight ){
			newWidth = (int) hWidth;
			newHeight = (int) hHeight;
		}
		else {
			// vertical trial
			newWidth = (int) (maxHeight * ratio);
			newHeight = maxHeight; 
		}
		
		return copy(image, gc.createCompatibleImage( newWidth, newHeight, transparency ));
	}


	public static BufferedImage getMessageTypeImage( MessageType messageType ) {
		switch ( messageType ) {
			case INFO:
				return load( "/images/app/info.PNG" );
			case WARNING:
				return load( "images/app/warning.PNG" );
			case ERROR:
				return load( "images/app/error.PNG" );
			default:
				return null;
		}
	}
	
}
